/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package trab.cg.phong.renderizador;

import java.awt.Color;
import java.awt.image.BufferedImage;

/**
 * Classe responsável pelo renderizador, onde reune toda as informações necessárias
 * para realizar o algoritmo de PHONG.
 * 
 * @author Deivide e Renan
 */
public class Renderizador
{
    // Atributos do ambiente.
    private Ambiente ambiente;
    // Atributos da camera.
    private Camera camera;
    //Atributos da superfície.
    private Superficie superficie;
    // Atributos da iluminação.
    private Iluminacao iluminacao;
    // Matriz imagem [i][j][R], [i][j][G] e [i][j][B]
    private double[][][] matrizImagem;
    // Matriz rotação.
    private double[][] matrizRotacao;
    
    // Inicia os valores de maior e menor para a normalização dos pixels.
    private double maiorRGB = 1;
    private double menorRGB = 0;
    
    // constantes maximo e minimo de cinza
    private double cinzaMin = 0;
    private double cinzaMax = 255;
    
    /**
     * Inicializa os objetos com os dados recebidos da interface gráfica.
     * 
     * @param c @see Camera.java
     * @param s @see Superficie.java
     * @param i @see Iluminacao.java
     * @param a @see Ambiente.java
     */
    public Renderizador(Camera c, Superficie s, Iluminacao i, Ambiente a)
    {
        this.ambiente = a;
        this.camera = c;
        this.superficie = s;
        this.iluminacao = i;
    }
    
    /**
     * Inicializa o objeto da matriz imagem.
     */
    private void inicializaMatrizImagem()
    {
        this.matrizImagem = new double[this.camera.getLargura()][this.camera.getAltura()][3];
    }
    
    /**
     * Inicializa a matriz rotação.
     */
    private void inicializaMatrizRotacao()
    {
        this.camera.setRotacaoX((this.camera.getRotacaoX() * Math.PI) / 180);
        this.camera.setRotacaoY((this.camera.getRotacaoY() * Math.PI) / 180);
        this.camera.setRotacaoZ((this.camera.getRotacaoZ() * Math.PI) / 180);
        
        // Obtém o valor dos cossenos.
        double cosRotacaoX = Math.cos(camera.getRotacaoX());
        double cosRotacaoY = Math.cos(camera.getRotacaoY());
        double cosRotacaoZ = Math.cos(camera.getRotacaoZ());
        
        // Obtém o valor dos senos.
        double senRotacaoX = Math.sin(camera.getRotacaoX());
        double senRotacaoY = Math.sin(camera.getRotacaoY());
        double senRotacaoZ = Math.sin(camera.getRotacaoZ());
        
        // Inicialização da matriz rotação.
        this.matrizRotacao = new double[3][3];
        
        // Linha 1.
        this.matrizRotacao[0][0] = (cosRotacaoY * cosRotacaoZ);
        this.matrizRotacao[0][1] = (cosRotacaoY * senRotacaoZ);
        this.matrizRotacao[0][2] = (- senRotacaoY);
        
        // Linha 2.
        this.matrizRotacao[1][0] = ((cosRotacaoZ * senRotacaoX * senRotacaoY) - (cosRotacaoX * senRotacaoZ));
        this.matrizRotacao[1][1] = ((senRotacaoX * senRotacaoY * senRotacaoZ) + (cosRotacaoX * cosRotacaoZ));
        this.matrizRotacao[1][2] = (cosRotacaoY * senRotacaoX);
        
        // Linha 3.
        this.matrizRotacao[2][0] = ((cosRotacaoX * senRotacaoY * cosRotacaoZ) + (senRotacaoX * senRotacaoZ));
        this.matrizRotacao[2][1] = ((cosRotacaoX * senRotacaoY * senRotacaoZ) - (senRotacaoX * cosRotacaoZ));
        this.matrizRotacao[2][2] = (cosRotacaoX * cosRotacaoY);
    }
    
    /**
     * Método que calcula o denominador dos valores de Z.
     * 
     * @param a
     * @param b
     * @return double - denominador.
     */
    private double calculaDenominador(double a, double b)
    {
        return ((2 * this.superficie.getF() * a)
                + (this.superficie.getB() * b * b)
                + (this.superficie.getC())
                + (2 * this.superficie.getE() * b)
                + (2 * this.superficie.getD() * a * b)
                + (this.superficie.getA() * a * a));
    }
    
    /**
     * Método que calcula o produto escalar de dois arrays.
     * 
     * @param array1
     * @param array2
     * @return double - produto escalar.
     */
    private double calculaProdutoEscalar(double[] array1, double[] array2)
    {
        return ((array1[0] * array2[0])
                + (array1[1] * array2[1])
                + (array1[2] * array2[2]));
    }
    
    /**
     * Método que calcula o módulo de um array.
     * 
     * @param array
     * @return double - módulo.
     */
    private double calculaModulo(double[] array)
    {
        return (Math.sqrt((array[0] * array[0])
                + (array[1] * array[1])
                + (array[2] * array[2])));
    }
    
    /**
     * Método de normalização.
     * 
     * @param i
     * @param j
     * @return valor normalizado.
     */
    private int[] normaliza(int i, int j)
    {
        double a = (255-0)/(this.maiorRGB - this.menorRGB);

        double b = 0 - (this.menorRGB * a);
       
        int red = (int)(this.matrizImagem[i][j][0] * a + b);
        int green = (int)(this.matrizImagem[i][j][1] * a + b);
        int blue = (int)(this.matrizImagem[i][j][2] * a + b);
              
        return (new int[] {red,green,blue});
        }

    
    /**
     * Método que calcula o cosseno através de dois arrays.
     * 
     * @param array1
     * @param array2
     * @return cosseno.
     */
    private double calculaCosseno(double[] array1, double[] array2)
    {
        double produtoEscalar = calculaProdutoEscalar(array1, array2);
        if (produtoEscalar < 0)
        {
            return 0;
        }
        
        double modulo = (calculaModulo(array1) * calculaModulo(array2));
        if (modulo == 0)
        {
            return 0;
        }
        
        double resultado = produtoEscalar / modulo;
        if (resultado < 0)
        {
            return 0;
        }
        else
        {
            return resultado;
        }
    }
    
    /**
     * Método que calcula o numerador da parte 1 dos valores de Z.
     * 
     * @param a
     * @param b
     * @return double
     */
    private double calculaNumerador1(double a, double b)
    {
        return ((- 2 * this.superficie.getD() * this.camera.getPosicaoX() * b)
                - (2 * this.superficie.getB() * this.camera.getPosicaoY() * b)
                - (2 * this.superficie.getJ())
                + (2 * this.superficie.getA() * a * a * this.camera.getPosicaoZ())
                - (2 * this.superficie.getA() * camera.getPosicaoX() * a)
                - (2 * this.superficie.getH() * b)
                + (2 * this.superficie.getF() * a * this.camera.getPosicaoZ())
                - (2 * this.superficie.getE() * this.camera.getPosicaoY())
                - (2 * this.superficie.getG() * a)
                + (2 * this.superficie.getE() * b * this.camera.getPosicaoZ())
                + (2 * this.superficie.getB() * b * b * this.camera.getPosicaoZ())
                - (2 * this.superficie.getF() * this.camera.getPosicaoX())
                - (2 * this.superficie.getD() * a * this.camera.getPosicaoY()))
                + (4 * this.superficie.getD() * a * b * this.camera.getPosicaoZ());
    }
    
    /**
     * Método que calcula o numerador da parte 2 dos valores de Z.
     * 
     * @param a
     * @param b
     * @return double - numerador parte 2.
     */
    private double calculaNumerador2(double a, double b)
    {
        double resultado = (- (2 * this.superficie.getC() * this.superficie.getG() * this.camera.getPosicaoX())
                + (Math.pow(this.superficie.getD(), 2) * Math.pow(this.camera.getPosicaoX(), 2) * Math.pow(b, 2))
                + (2 * this.superficie.getJ() * this.superficie.getH() * b)
                - (2 * this.superficie.getE() * b * this.superficie.getK())
                + (Math.pow(this.superficie.getJ(), 2))
                - (this.superficie.getC() * this.superficie.getK())
                + (Math.pow(this.superficie.getF(), 2) * Math.pow(a, 2) * Math.pow(this.camera.getPosicaoZ(), 2))
                + (Math.pow(this.superficie.getE(), 2) * Math.pow(b, 2) * Math.pow(this.camera.getPosicaoZ(), 2))
                + (Math.pow(this.superficie.getD(), 2) * Math.pow(a, 2) * Math.pow(this.camera.getPosicaoY(), 2))
                + (2 * this.superficie.getJ() * this.superficie.getE() * this.camera.getPosicaoY())
                + (2 * this.superficie.getJ() * this.superficie.getG() * a)
                + (2 * this.superficie.getJ() * this.superficie.getF() * this.camera.getPosicaoX())
                - (2 * this.superficie.getF() * a * this.superficie.getK())
                - (this.superficie.getB() * Math.pow(b, 2) * this.superficie.getK())
                - (2 * this.superficie.getC() * this.superficie.getH() * this.camera.getPosicaoY())
                - (this.superficie.getC() * this.superficie.getA() * Math.pow(this.camera.getPosicaoX(), 2))
                - (this.superficie.getC() * this.superficie.getB() * Math.pow(this.camera.getPosicaoY(), 2))
                - (this.superficie.getA() * Math.pow(a, 2) * this.superficie.getK())
                + (2 * this.superficie.getD() * this.camera.getPosicaoX() * Math.pow(b, 2) * this.superficie.getH())
                - (2 * this.superficie.getD() * this.camera.getPosicaoX() * b * this.superficie.getF() * a * this.camera.getPosicaoZ())
                - (2 * this.superficie.getD() * this.camera.getPosicaoX() * b * this.superficie.getE() * this.camera.getPosicaoY())
                - (2 * this.superficie.getD() * this.camera.getPosicaoX() * b * this.superficie.getG() * a)
                + (2 * this.superficie.getD() * this.camera.getPosicaoX() * Math.pow(b, 2) * this.superficie.getE() * this.camera.getPosicaoZ())
                + (2 * this.superficie.getD() * Math.pow(this.camera.getPosicaoX(), 2) * b * this.superficie.getF())
                - (2 * Math.pow(this.superficie.getD(), 2) * this.camera.getPosicaoX() * b * a * this.camera.getPosicaoY())
                + (2 * this.superficie.getB() * this.camera.getPosicaoY() * b * this.superficie.getA() * this.camera.getPosicaoX() * a)
                + (2 * this.superficie.getB() * this.camera.getPosicaoY() * b * this.superficie.getF() * a * this.camera.getPosicaoZ())
                + (2 * this.superficie.getB() * this.camera.getPosicaoY() * b * this.superficie.getG() * a)
                + (2 * this.superficie.getB() * this.camera.getPosicaoY() * b * this.superficie.getF() * this.camera.getPosicaoX())
                + (Math.pow(this.superficie.getH(), 2) * Math.pow(b, 2))
                + (Math.pow(this.superficie.getE(), 2) * Math.pow(this.camera.getPosicaoY(), 2))
                + (Math.pow(this.superficie.getG(), 2) * Math.pow(a, 2))
                + (this.superficie.getF() * 2 * Math.pow(this.camera.getPosicaoX(), 2))
                - (2 * this.superficie.getA() * Math.pow(a, 2) * this.camera.getPosicaoZ() * this.superficie.getE() * this.camera.getPosicaoY())
                + (2 * this.superficie.getA() * this.camera.getPosicaoX() * a * this.superficie.getH() * b)
                + (2 * this.superficie.getA() * this.camera.getPosicaoX() * a * this.superficie.getE() * this.camera.getPosicaoY())
                + (2 * this.superficie.getA() * this.camera.getPosicaoX() * a * this.superficie.getE() * b * this.camera.getPosicaoZ())
                + (2 * this.superficie.getH() * b * this.superficie.getF() * a * this.camera.getPosicaoZ())
                - (2 * this.superficie.getH() * b * this.superficie.getE() * this.camera.getPosicaoY())
                + (2 * this.superficie.getH() * b * this.superficie.getG() * a)
                + (2 * this.superficie.getH() * Math.pow(b, 2) * this.superficie.getE() * this.camera.getPosicaoZ())
                + (2 * this.superficie.getH() * b * this.superficie.getF() * this.camera.getPosicaoX())
                - (2 * this.superficie.getH() * b * this.superficie.getD() * a * this.camera.getPosicaoY())
                - (2 * this.superficie.getF() * a * this.camera.getPosicaoZ() * this.superficie.getE() * this.camera.getPosicaoY())
                + (2 * this.superficie.getF() * Math.pow(a, 2) * this.camera.getPosicaoZ() * this.superficie.getG())
                + (2 * this.superficie.getF() * a * Math.pow(this.camera.getPosicaoZ(), 2) * this.superficie.getE() * b)
                - (2 * Math.pow(this.superficie.getF(), 2) * a * this.camera.getPosicaoZ() * this.camera.getPosicaoX())
                + (2 * this.superficie.getF() * Math.pow(a, 2) * this.camera.getPosicaoZ() * this.superficie.getD() * this.camera.getPosicaoY())
                + (2 * this.superficie.getE() * this.camera.getPosicaoY() * this.superficie.getG() * a)
                - (2 * Math.pow(this.superficie.getE(), 2) * this.camera.getPosicaoY() * b * this.camera.getPosicaoZ())
                + (2 * this.superficie.getE() * this.camera.getPosicaoY() * this.superficie.getF() * this.camera.getPosicaoX())
                + (2 * this.superficie.getE() * Math.pow(this.camera.getPosicaoY(), 2) * this.superficie.getD() * a)
                - (2 * this.superficie.getE() * this.camera.getPosicaoY() * this.superficie.getD() * a * b * this.camera.getPosicaoZ())
                + (2 * this.superficie.getG() * a * this.superficie.getE() * b * this.camera.getPosicaoZ())
                - (2 * this.superficie.getG() * a * this.superficie.getF() * this.camera.getPosicaoX())
                + (2 * this.superficie.getG() * Math.pow(a, 2) * this.superficie.getD() * this.camera.getPosicaoY())
                - (2 * this.superficie.getE() * b * this.camera.getPosicaoZ() * this.superficie.getF() * this.camera.getPosicaoX())
                - (2 * this.superficie.getB() * Math.pow(b, 2) * this.camera.getPosicaoZ() * this.superficie.getF() * this.camera.getPosicaoX())
                - (2 * this.superficie.getF() * this.camera.getPosicaoX() * this.superficie.getD() * a * this.camera.getPosicaoY())
                + (2 * this.superficie.getD() * this.camera.getPosicaoX() * b * this.superficie.getJ())
                + (2 * this.superficie.getB() * this.camera.getPosicaoY() * b * this.superficie.getJ())
                - (2 * this.superficie.getJ() * this.superficie.getA() * Math.pow(a, 2) * this.camera.getPosicaoZ())
                + (2 * this.superficie.getJ() * this.superficie.getA() * this.camera.getPosicaoX() * a)
                - (2 * this.superficie.getJ() * this.superficie.getF() * a * this.camera.getPosicaoZ())
                - (2 * this.superficie.getJ() * this.superficie.getE() * b * this.camera.getPosicaoZ())
                - (2 * this.superficie.getJ() * this.superficie.getB() * Math.pow(b, 2) * this.camera.getPosicaoZ())
                + (2 * this.superficie.getJ() * this.superficie.getD() * a * this.camera.getPosicaoY())
                - (4 * this.superficie.getJ() * this.superficie.getD() * a * b * this.camera.getPosicaoZ())
                - (4 * this.superficie.getF() * a * this.superficie.getH() * this.camera.getPosicaoY())
                - (2 * this.superficie.getF() * a * this.superficie.getB() * Math.pow(this.camera.getPosicaoY(), 2))
                - (2 * this.superficie.getB() * Math.pow(b, 2) * this.superficie.getG() * this.camera.getPosicaoX())
                - (this.superficie.getB() * Math.pow(b, 2) * this.superficie.getA() * Math.pow(this.camera.getPosicaoX(), 2))
                - (4 * this.superficie.getE() * b * this.superficie.getG() * this.camera.getPosicaoX())
                - (2 * this.superficie.getE() * b * this.superficie.getA() * Math.pow(this.camera.getPosicaoX(), 2))
                - (2 * this.superficie.getA() * Math.pow(a, 2) * this.superficie.getH() * this.camera.getPosicaoY())
                - (this.superficie.getA() * Math.pow(a, 2) * this.superficie.getB() * Math.pow(this.camera.getPosicaoY(), 2))
                - (2 * this.superficie.getC() * this.superficie.getD() * this.camera.getPosicaoX() * this.camera.getPosicaoY())
                - (this.superficie.getC() * this.superficie.getB() * Math.pow(b, 2) * Math.pow(this.camera.getPosicaoZ(), 2))
                - (this.superficie.getC() * this.superficie.getA() * Math.pow(a, 2) * Math.pow(this.camera.getPosicaoZ(), 2))
                + (2 * this.superficie.getC() * this.superficie.getA() * this.camera.getPosicaoX() * a * this.camera.getPosicaoZ())
                + (2 * this.superficie.getC() * this.superficie.getH() * b * this.camera.getPosicaoZ())
                - (2 * this.superficie.getC() * this.superficie.getD() * a * Math.pow(this.camera.getPosicaoZ(), 2) * b)
                + (2 * this.superficie.getC() * this.superficie.getB() * this.camera.getPosicaoY() * b * this.camera.getPosicaoZ())
                + (2 * this.superficie.getC() * this.superficie.getD() * this.camera.getPosicaoX() * b * this.camera.getPosicaoZ())
                + (2 * this.superficie.getC() * this.superficie.getG() * a * this.camera.getPosicaoZ())
                + (2 * this.superficie.getC() * this.superficie.getD() * a * this.camera.getPosicaoZ() * this.camera.getPosicaoY())
                - (2 * this.superficie.getD() * a * b * this.superficie.getK()));
        
        // Verificar se o retorno for negativo.
        return (resultado >= 0) ? Math.sqrt(resultado) : -1;
    }
    
    /**
     * Método que executa o algoritmo de Phong.
     */
    private void algoritmoPhong()
    {
        // Variáveis de conversão do sistema de tela para o sistema espaço-imagem.
        double meiaLargura = this.camera.getLargura() / 2;
        double meiaAltura = this.camera.getAltura() / 2;
        double lambdaX = 1 / this.camera.getPixelX();
        double lambdaY = 1 / this.camera.getPixelY();
        
        for (int i = 0; i < this.camera.getLargura(); i++)
        {
            for (int j = 0; j < this.camera.getAltura(); j++)
            {
                // Conversão do sistema de tela para o sistema espaço-imagem.
                double x = (double)(lambdaX * (i - meiaLargura));
                double y = - (double)(lambdaY * (j - meiaAltura));
                
                // Cálculo dos valores de A e B 
                double a = (this.matrizRotacao[0][0] * x)
                        + (this.matrizRotacao[1][0] * y)
                        + (this.matrizRotacao[2][0] * this.camera.getDistanciaFocal());
                a = a / ((this.matrizRotacao[0][2] * x)
                        + (this.matrizRotacao[1][2] * y )
                        + (this.matrizRotacao[2][2] * this.camera.getDistanciaFocal()));
                
                double b = (this.matrizRotacao[0][1] * x)
                        + (this.matrizRotacao[1][1] * y)
                        + (this.matrizRotacao[2][1] * this.camera.getDistanciaFocal());
                b = b / ((this.matrizRotacao[0][2] * x)
                        + (this.matrizRotacao[1][2] * y)
                        + (this.matrizRotacao[2][2] * this.camera.getDistanciaFocal()));
             
                // Cálculo de z1 e z2 
                double denominadorZ = calculaDenominador(a, b);
                
                if (denominadorZ != 0)
                {
                    double numerador = Math.sqrt(calculaNumerador2(a, b));
                    
                    if (numerador > -1)
                    {
                        double z1 = calculaNumerador1(a, b);
                        double z2 = z1;
                        
                        z1 = (0.5 * (z1 + 2 * numerador)) / denominadorZ;
                        z2 = (0.5 * (z2 - 2 * numerador)) / denominadorZ;
                        
                        // Cálculo da interseção do raio oriundo da câmera com a superfície dada.
                        double intersecaoX1 = this.camera.getPosicaoX() + (z1 - this.camera.getPosicaoZ()) * a;
                        double intersecaoY1 = this.camera.getPosicaoY() + (z1 - this.camera.getPosicaoZ()) * b;
                        
                        double intersecaoX2 = this.camera.getPosicaoX() + (z2 - this.camera.getPosicaoZ()) * a;
                        double intersecaoY2 = this.camera.getPosicaoY() + (z2 - this.camera.getPosicaoZ()) * b;
                        
                        // Cálculo da distância entre o CP e o ponto (x1, y1, z1) ou (x2, y2, z2).
                        double distancia1 = Math.sqrt(Math.pow(this.camera.getPosicaoX() - intersecaoX1, 2) + Math.pow(this.camera.getPosicaoY() - intersecaoY1, 2) + Math.pow(this.camera.getPosicaoZ() - z1, 2));
                        double distancia2 = Math.sqrt(Math.pow(this.camera.getPosicaoX() - intersecaoX2, 2) + Math.pow(this.camera.getPosicaoY() - intersecaoY2, 2) + Math.pow(this.camera.getPosicaoZ() - z2, 2));
                        
                        // print intersecaoX1, intersecaoY1, distancia1, a, b.
                        
                        double[] vetor = new double[3];
                        
                        // Decide sobre qual o menor.
                        if (distancia1 < distancia2)
                        {
                            vetor[0] = intersecaoX1;
                            vetor[1] = intersecaoY1;
                            vetor[2] = z1;
                        }
                        else
                        {
                            vetor[0] = intersecaoX2;
                            vetor[1] = intersecaoY2;
                            vetor[2] = z2;
                        }

                        // Calcula vetor Normal.
                        double[] vetorNormal = new double[3];
                        
                        vetorNormal[0] = (2 * this.superficie.getA() * vetor[0])
                                + (2 * this.superficie.getD() * vetor[1])
                                + (2 * this.superficie.getF() * vetor[2])
                                + (2 * this.superficie.getG());
                        vetorNormal[1] = (2 * this.superficie.getB() * vetor[1])
                                + (2 * this.superficie.getD() * vetor[0])
                                + (2 * this.superficie.getE() * vetor[2])
                                + (2 * this.superficie.getH());
                        vetorNormal[2] = (2 * this.superficie.getC() * vetor[2])
                                + (2 * this.superficie.getE() * vetor[1])
                                + (2 * this.superficie.getF() * vetor[0])
                                + (2 * this.superficie.getJ());
                        
                        // Calcula vetor Direcao da Fonte de Luz.
                        double[] vetorFonteLuz = new double[3];
                        vetorFonteLuz[0] = (this.iluminacao.getPosicaoX() - vetor[0]);
                        vetorFonteLuz[1] = (this.iluminacao.getPosicaoY() - vetor[1]);
                        vetorFonteLuz[2] = (this.iluminacao.getPosicaoZ() - vetor[2]);
                        
                        // Calcula vetor Direcao do Observador.
                        double[] vetorObservador = new double[3];
                        vetorObservador[0] = (this.camera.getPosicaoX() - vetor[0]);
                        vetorObservador[1] = (this.camera.getPosicaoY() - vetor[1]);
                        vetorObservador[2] = (this.camera.getPosicaoZ() - vetor[2]);
                        
                        // Calcula vetor Reflexão.
                        //double produtoEscalar = calculaProdutoEscalar(vetorNormal, vetorFonteLuz);
                        
                        double ln = vetorFonteLuz[0]*vetorNormal[0] + vetorFonteLuz[1]*vetorNormal[1] + vetorFonteLuz[2]*vetorNormal[2];
                        
                        double[] vetorReflexao = new double[3];
                        vetorReflexao[0] = ((2 * ln) * vetorNormal[0] - vetorFonteLuz[0]);
                        vetorReflexao[1] = ((2 * ln) * vetorNormal[1] - vetorFonteLuz[1]);
                        vetorReflexao[2] = ((2 * ln) * vetorNormal[2] - vetorFonteLuz[2]);
                        
                        // Calculo do angulo teta.
                        double cossenoTeta = calculaCosseno(vetorFonteLuz, vetorNormal);
                        
                        // Calculo do angulo alfa.
                        double cossenoAlfa = calculaCosseno(vetorReflexao, vetorObservador);
                        
                        // Calcula o ângulo elevado necessário para o calculo da iluminação.
                        double anguloElevado = Math.pow(cossenoAlfa, this.iluminacao.getExpoenteEspecular());
                        
                        // Aplicando o modelo de Iluminação de Phong para a banda R, G e B.
                        this.matrizImagem[i][j][0] = (this.ambiente.getR() * this.iluminacao.getAmbienteR())
                                + (this.iluminacao.getCorLuzR())
                                * (this.iluminacao.getDifusaoR() * cossenoTeta)
                                + (this.iluminacao.getEspecularR() * anguloElevado);
                        
                        this.matrizImagem[i][j][1] = (this.ambiente.getG() * this.iluminacao.getAmbienteG())
                                + (this.iluminacao.getCorLuzG()) 
                                * (this.iluminacao.getDifusaoG() * cossenoTeta)
                                + (this.iluminacao.getEspecularG() * anguloElevado);
                        
                        this.matrizImagem[i][j][2] = (this.ambiente.getB() * this.iluminacao.getAmbienteB())
                                + (this.iluminacao.getCorLuzB()) 
                                * (this.iluminacao.getDifusaoB() * cossenoTeta)
                                + (this.iluminacao.getEspecularB() * anguloElevado);
                        
                        this.menorRGB = Math.min(Math.min(this.matrizImagem[i][j][0], this.matrizImagem[i][j][1]), Math.min(this.matrizImagem[i][j][2], this.menorRGB));
                        this.maiorRGB = Math.max(Math.max(this.matrizImagem[i][j][0], this.matrizImagem[i][j][1]), Math.max(this.matrizImagem[i][j][2], this.maiorRGB));
                    }
                    // Caso numerador parte 2 < 0.
                    else
                    {
                        this.matrizImagem[i][j][0] = 0;
                        this.matrizImagem[i][j][1] = 0;
                        this.matrizImagem[i][j][2] = 0;
                    }
                }
                // Caso divisor == 0.
                else
                {
                    this.matrizImagem[i][j][0] = 0;
                    this.matrizImagem[i][j][1] = 0;
                    this.matrizImagem[i][j][2] = 0;
                }
            }
        }
    }
    
    /**
     * Método responsável por criar a imagem através da chamada dos métodos
     * declarados anteriormente.
     */
    public BufferedImage renderizar()
    {
        // Inicialização da matriz imagem.
        inicializaMatrizImagem();
        
        // Inicialização da matriz rotação.
        inicializaMatrizRotacao();
        
        // Chamada ao algoritmo de Phong.
        algoritmoPhong();
        
        int[] vetorRGB;
        
        // Cria a imagem gerada através do algoritmo de phong.
        BufferedImage imagem = new BufferedImage(this.camera.getLargura(), this.camera.getAltura(), BufferedImage.TYPE_INT_RGB);
        for (int i = 0; i < this.camera.getLargura(); i++)
        {
            for (int j = 0; j < this.camera.getAltura(); j++)
            {
                vetorRGB = normaliza(i, j);
                imagem.setRGB(i, j, new Color(vetorRGB[0], vetorRGB[1], vetorRGB[2]).getRGB());
            }
        }
        
        return imagem;
    }
}
